package com.jiuqi.abc.calculator.service;

import com.jiuqi.abc.calculator.dao.ABCResultBean;
import com.jiuqi.abc.calculator.dao.ABCStatus;
import com.jiuqi.abc.intf.ABCIntfException;
import com.jiuqi.abc.intf.IntfFactory;
import com.jiuqi.abc.intf.provider.unit.IUnitProvider;
import com.jiuqi.abc.intf.provider.unit.UnitObj;
import com.jiuqi.abc.intf.provider.unit.ValidateAndOutputUnitObj;
import com.jiuqi.abc.model.enums.ValidateStatusEnum;
import com.jiuqi.abc.model.service.ABCModelDWService;
import com.jiuqi.abc.model.service.ABCOutputTableService;
import com.jiuqi.abc.model.sysoption.ABCSystemOptionValueService;
import com.jiuqi.abc.model.vo.ValidateAndOutputTreeVo;
import com.jiuqi.bi.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 单位service
 *
 * @author 龚泽楷
 * @date 2023年05月31日 17:36
 */
@Service
public class ABCUnitService {

    @Autowired
    private IntfFactory intfFactory;

    @Autowired
    private ABCSystemOptionValueService systemOptionValueService;

    @Autowired
    private ABCResultService abcResultService;

    @Autowired
    private ABCOutputTableService abcOutputTableService;

    @Autowired
    private ABCDataValidatorService abcDataValidatorService;

    @Autowired
    private ABCModelDWService abcModelDWService;

    Logger logger = LoggerFactory.getLogger(ABCUnitService.class);


    /**
     * 获取 指定时期参数 下 指定单位 的 下级单位集合
     *
     * @param period   指定时期参数
     * @param unitCode 指定单位，当unitCode为null时，获取根节点
     * @return 下级单位集合
     */
    public List<ValidateAndOutputTreeVo> getChildren(String period, String unitCode) throws ABCIntfException {
        // 获取当前单位所有下级单位，如果unitCode为null则为单位树形的根节点
        List<UnitObj> unitObjList = getUnitProvider().getChildren(period, "null".equals(unitCode) ? "" : unitCode);
        //获取当前时期下 已经匹配模型的单位编码集合
        List<String> hasModelUnitCodeList = abcModelDWService.queryByPeriod(period);

        //遍历当前单位的所有下级单位
        List<ValidateAndOutputTreeVo> treeNodeVoList = new ArrayList<>();
        for (UnitObj unitObj : unitObjList) {
            ABCResultBean abcResultBean = abcResultService.getLast(period, unitObj.getCode());
            //判断是否为叶子结点
            List<UnitObj> testObjList = new ArrayList<>();
            try {
                testObjList = getUnitProvider().getChildren(period, unitObj.getCode());
            } catch (ABCIntfException e) {
                logger.error("获取下级单位异常", e);
            }
            boolean isLeaf = testObjList.isEmpty();

            //将 单位实体对象  转换为  树形节点前端展示对象
            ValidateAndOutputTreeVo treeNodeVo = new ValidateAndOutputTreeVo(unitObj.getCode(), unitObj.getName(), unitObj.getParent(), isLeaf);

            boolean hasModel = hasModelUnitCodeList.contains(unitObj.getCode());
            ValidateStatusEnum validateStatusEnum;
            boolean canOutput;

            //避免 模型已经删除，模型存在时的分摊结果记录还存在  的情况
            if (hasModel){
                validateStatusEnum = getValidateStatusEnum(abcResultBean);
                canOutput = checkCanOutput(abcResultBean);
            }else {
                validateStatusEnum = ValidateStatusEnum.OTHER;
                canOutput = false;
            }

            //只要有计算结果便可以进行公式解析
            boolean hasResult = checkHasResult(abcResultBean);

            treeNodeVo.setHasModel(hasModel);
            treeNodeVo.setValidateStatusEnum(validateStatusEnum);
            treeNodeVo.setCanOutput(canOutput);
            treeNodeVo.setHasResult(hasResult);

            treeNodeVoList.add(treeNodeVo);
        }

        return treeNodeVoList;
    }


    /**
     * 获取 指定时期参数 下 全部单位项
     *
     * @param period 时期参数
     * @return 全部单位项
     */
    public Map<String, List<ValidateAndOutputUnitObj>> getWholeUnit(String period) throws ABCIntfException {
        List<UnitObj> unitAll = getUnitProvider().getAll(period);

        return sortUnitByIsShared(period, unitAll,false);
    }


    /**
     * 在指定 时期参数 下 根据 单位名称 模糊匹配 获取 匹配后单位集合
     *
     * @param period    时期参数
     * @param searchKey 搜索内容 （unitCode、unitName）
     * @return 匹配后单位集合
     */
    public List<ValidateAndOutputUnitObj> fuzzyMatchByUnitName(String period, String searchKey) {
        List<ValidateAndOutputUnitObj> matchedResult = new ArrayList<>();
        try {
            List<UnitObj> searchedList = getUnitProvider().search(period, searchKey);

            Map<String, List<ValidateAndOutputUnitObj>> sortedResultMap = sortUnitByIsShared(period, searchedList,true);
            matchedResult.addAll(sortedResultMap.get("sharedUnitList"));
            matchedResult.addAll(sortedResultMap.get("unSharedUnitList"));

        } catch (ABCIntfException e) {
            logger.error("模糊匹配获取单位异常", e);
        }
        return matchedResult;
    }


    private IUnitProvider getUnitProvider() throws ABCIntfException {
        String categoryName = systemOptionValueService.getABCOrgOptionValue();
        if (StringUtils.isEmpty(categoryName)) {
            throw new ABCIntfException("请配置系统选项【abc组织机构类型】");
        }
        return intfFactory.createUnitProvider(categoryName);
    }


    /**
     * 在 特定时期参数 下 根据 是否分摊 对 单位集合 进行划分
     * @param period        特定时期参数
     * @param allUnits      单位集合
     * @param isSearch      是否 为搜索方法 调用，若 不为搜索方法调用 则需过滤 未匹配模型的单位
     * @return 划分后集合信息
     */
    public Map<String, List<ValidateAndOutputUnitObj>> sortUnitByIsShared(String period, List<UnitObj> allUnits,boolean isSearch) {
        List<UnitObj> sharedUnitList = new ArrayList<>();
        List<UnitObj> unSharedUnitList = new ArrayList<>();
        List<ValidateAndOutputUnitObj> sharedValidateAndOutputObjList = new ArrayList<>();
        List<ValidateAndOutputUnitObj> unSharedValidateAndOutputObjList = new ArrayList<>();

        //获取当前时期下 已经匹配模型的单位编码集合(集合内部不重复，同一时期同一单位只能关联同一模型)
        List<String> hasModelUnitCodeList = abcModelDWService.queryByPeriod(period);

        //获取当前时期下 最近的已经分摊对象集合
        Map<String,ABCResultBean> latestSharedUnitMap = new HashMap<>();
        List<ABCResultBean> resultBeanList = abcResultService.listLast(period, "");
        resultBeanList.forEach(resultBean -> latestSharedUnitMap.put(resultBean.getUnitCode(), resultBean));

        //进行单位划分
        allUnits.stream()
                .filter(unitObj -> {
                    if (isSearch)
                        return true;
                    else
                        return hasModelUnitCodeList.contains(unitObj.getCode());
                })
                .forEach(sortItem -> {
                    String sortItemCode = sortItem.getCode();

                    //获取 是否有子单位
                    List<UnitObj> testObjList = new ArrayList<>();
                    try {
                        testObjList = getUnitProvider().getChildren(period, sortItemCode);
                    } catch (ABCIntfException e) {
                        logger.error("获取下级单位异常", e);
                    }
                    sortItem.setHasChildren(!testObjList.isEmpty());


                    if (Objects.nonNull(latestSharedUnitMap.get(sortItemCode)))
                        sharedUnitList.add(sortItem);
                    else
                        unSharedUnitList.add(sortItem);

                });

        //处理已分摊单位
        sharedUnitList.forEach(sharedUnit -> {
            ValidateAndOutputUnitObj validateAndOutputUnitObj = new ValidateAndOutputUnitObj();
            ABCResultBean abcResultBean = latestSharedUnitMap.get(sharedUnit.getCode());

            //设置 继承于父类的基础信息
            BeanUtils.copyProperties(sharedUnit, validateAndOutputUnitObj);


            if (hasModelUnitCodeList.contains(validateAndOutputUnitObj.getCode())) {
                //设置 指定时期参数下某一单位对应模型的审核状态
                ValidateStatusEnum validateStatusEnum = getValidateStatusEnum(abcResultBean);
                validateAndOutputUnitObj.setValidateStatusEnum(validateStatusEnum);
                //设置 是否匹配模型
                validateAndOutputUnitObj.setHasModel(true);
            }else {
                //设置 指定时期参数下某一单位对应模型的审核状态
                validateAndOutputUnitObj.setValidateStatusEnum(ValidateStatusEnum.OTHER);
                //设置 是否匹配模型
                validateAndOutputUnitObj.setHasModel(false);
            }

            //设置 是否可以输出
            boolean canOutput = checkCanOutput(abcResultBean);
            validateAndOutputUnitObj.setCanOutput(canOutput);

            //设置 是否有计算结果
            boolean hasResult = checkHasResult(abcResultBean);
            validateAndOutputUnitObj.setHasResult(hasResult);

            //加入集合
            sharedValidateAndOutputObjList.add(validateAndOutputUnitObj);

        });

        //处理未分摊单位
        unSharedUnitList.forEach(unSharedUnit -> {
            ValidateAndOutputUnitObj validateAndOutputUnitObj = new ValidateAndOutputUnitObj();

            //设置 继承于父类的基础信息
            BeanUtils.copyProperties(unSharedUnit, validateAndOutputUnitObj);

            //设置 指定时期参数下某一单位对应模型的审核状态
            validateAndOutputUnitObj.setValidateStatusEnum(ValidateStatusEnum.OTHER);

            //设置 是否匹配模型
            validateAndOutputUnitObj.setHasModel(hasModelUnitCodeList.contains(validateAndOutputUnitObj.getCode()));

            //设置 是否可以输出
            validateAndOutputUnitObj.setCanOutput(false);

            //设置 是否有计算结果
            validateAndOutputUnitObj.setHasResult(false);

            //加入集合
            unSharedValidateAndOutputObjList.add(validateAndOutputUnitObj);
        });

        //返回划分结果
        HashMap<String, List<ValidateAndOutputUnitObj>> result = new HashMap<>();
        result.put("unSharedUnitList", unSharedValidateAndOutputObjList);
        result.put("sharedUnitList", sharedValidateAndOutputObjList);

        return result;
    }


    /**
     * 根据 分摊对象 获取 数据审核状态
     *
     * @param abcResultBean 分摊对象
     * @return 数据审核状态
     */
    private ValidateStatusEnum getValidateStatusEnum(ABCResultBean abcResultBean) {
        if (Objects.nonNull(abcResultBean)) {
            ABCStatus abcStatus = abcResultBean.getStatus();

            if (abcStatus == ABCStatus.CALCULATING) {
                return ValidateStatusEnum.PROCESS;
            } else if (abcStatus == ABCStatus.SUCCESS) {
                return ValidateStatusEnum.SUCCESS;
            } else {
                boolean hasValidateMessage = abcDataValidatorService.hasValidateMessageByResultGuid(abcResultBean.getId());
                if (hasValidateMessage)
                    return ValidateStatusEnum.WARNING;
                else
                    return ValidateStatusEnum.ERROR;
            }
        } else {

            return ValidateStatusEnum.OTHER;
        }

    }


    /**
     * 根据 分摊对象 判断 是否可以 输出
     *
     * @param abcResultBean 分摊对象
     * @return 是否可以 输出
     */
    private boolean checkCanOutput(ABCResultBean abcResultBean) {
        if (Objects.nonNull(abcResultBean)) {

            //查询 当前模型 是否存在输出表

            boolean hasOutputTables = abcOutputTableService.hasByModelGuid(abcResultBean.getModelId());
            ABCStatus abcStatus = abcResultBean.getStatus();

            return hasOutputTables && (ABCStatus.SUCCESS == abcStatus || ABCStatus.WARNING == abcStatus);
        } else {

            return false;

        }

    }

    /**
     * 根据 分摊对象 判断 是否 有计算结果
     *
     * @param abcResultBean 分摊对象
     * @return 是否有计算结果
     */
    private boolean checkHasResult(ABCResultBean abcResultBean) {
        if (Objects.nonNull(abcResultBean) && !StringUtils.isEmpty(abcResultBean.getOssGuid())) {
            ABCStatus abcStatus = abcResultBean.getStatus();
            return ABCStatus.SUCCESS == abcStatus || ABCStatus.WARNING == abcStatus;
        }

        return false;
    }

    /**
     * 获取溯源路径
     * @param period    时期参数
     * @param unitCode  单位编码
     * @return
     */
    public List<String> getPathById(String period, String unitCode) {
        List<String> path = new ArrayList<>();
        try {
        while (StringUtils.isNotEmpty(unitCode)){
            UnitObj unitObj = getUnitProvider().get(period,unitCode);

            if(unitObj != null){
                path.add(unitCode);
                unitCode = unitObj.getParent();
            }else{
                unitCode = null;
            }
        }
        Collections.reverse(path);
        } catch (ABCIntfException e) {
            logger.error("获取单位异常", e);
        }
        return path;
    }
}
